package deepr.core;

import deepr.anno.Property;
import deepr.anno.Schema;
import deepr.anno.Type;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;

import java.io.File;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.*;

@Slf4j
public class SchemaScanner {


  public static void main(String[] args) throws Exception {
    SchemaScanner.scan("deepr");
  }

  public static void scan(String path) throws Exception {
    //入参 要扫描的包名
    path = path.replaceAll("\\.", "/");
    Enumeration<URL> dirs = Thread.currentThread().getContextClassLoader().getResources(path);
    while (dirs.hasMoreElements()) {
      URL url = dirs.nextElement();
      if (url.getProtocol().equals("file")) {
        listFiles(new File(url.getFile()), path);
      }
    }
    SchemaManager.schemaStore.forEach((key, value) -> {
      log.info("schema: {}, fields: {}", key, value);
    });
    SchemaManager.objectStore.forEach((key, value) -> {
      log.info("object: {}, fields: {}", key, value);
    });
  }


  private static void listFiles(File dir, String path) throws Exception {
    if (dir.isDirectory()) {
      for (File f : dir.listFiles()) {
        listFiles(f, path);
      }
    } else {
      if (dir.getName().endsWith(".class")) {
        loadClass(dir, path);
      }
    }
  }

  private static void loadClass(File file, String scan) throws Exception {
    // 因为scan 就是/  ， 所有把 file的 / 转成  \   统一都是：  /
    String fPath = file.getAbsolutePath().replaceAll("\\\\", "/");
    // 把 包路径 前面的 盘符等 去掉 ， 这里必须是lastIndexOf ，防止名称有重复的
    String packageName = fPath.substring(fPath.lastIndexOf(scan));
    // 去掉后缀.class ，并且把 / 替换成 .    这样就是  com.hadluo.A 格式了 ， 就可以用Class.forName加载了
    packageName = packageName.replace(".class", "").replaceAll("/", ".");
    // 根据名称加载类
    parseClass(Class.forName(packageName));
  }


  public static void parseClass(Class<?> czz) {
    //入参 要扫描的包名
    Schema schema = czz.getAnnotation(Schema.class);
    if (schema == null)
      return;
    String name = schema.name();
    Field[] fields = czz.getDeclaredFields();
    SchemaData schemaData = new SchemaData();
    HashMap<String, PropertyData> fieldMap = new HashMap<>();
    for (Field field : fields) {
      Property property = field.getAnnotation(Property.class);
      if (property == null)
        continue;
      PropertyData propertyData = new PropertyData();
      String typeStr = field.getType().toString();
      Type proType;
      String fieldName = field.getName();
      if (!property.name().equals(""))
        fieldName = property.name();
      propertyData.setName(fieldName);
      if (property.schema() != Schema.class) {
        propertyData.setSchema(property.schema());
        propertyData.setPoint(property.point());
        String relationLabel = property.relationLabel();
        if (!property.relationLabel().equals("")) {
          propertyData.setRelation(property.relation());
          propertyData.setRelationLabel(relationLabel);
          propertyData.setRelationObject(StringUtils.uncapitalize(relationLabel));
        }
        if (!property.forProperty().equals("")) {
          propertyData.setForProperty(property.forProperty());
        }
      }
      if (property.type() != Type.NULL) {
        proType = property.type();
      } else if (typeStr.endsWith("String")) {
        proType = Type.STRING;
      } else if (typeStr.endsWith("Integer")) {
        proType = Type.INTEGER;
      } else if (typeStr.endsWith("Double")) {
        proType = Type.DOUBLE;
      } else if (typeStr.endsWith("Long")) {
        proType = Type.LONG;
      } else if (typeStr.endsWith("Date")) {
        proType = Type.DATE;
      } else if (typeStr.endsWith("Timestamp")) {
        proType = Type.TIMESTAMP;
      } else if (typeStr.endsWith("BigDecimal")) {
        proType = Type.LONG;
      } else if (typeStr.endsWith("Float")) {
        proType = Type.FLOAT;
      } else {
        proType = Type.JSON;
      }
      propertyData.setType(proType);
      if (property.handler() != Schema.class) {
        propertyData.setHandler(property.handler());
        if (property.handlerArgs().length == 0)
          propertyData.setHandlerArgs(new String[]{fieldName});
        else
          propertyData.setHandlerArgs(property.handlerArgs());
      }
      propertyData.setIsPrimaryKey(property.primaryKey());
      if (property.primaryKey()) {
        schemaData.setPrimaryKey(fieldName);
      }
      fieldMap.put(fieldName, propertyData);
    }
    schemaData.setSchemaType(schema.type());
    schemaData.setFields(fieldMap);
    schemaData.setName(name);
    schemaData.setObject();
    SchemaManager.schemaStore.put(czz, schemaData);
    SchemaManager.objectStore.put(StringUtils.uncapitalize(name), schemaData);
  }


}
